<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta name="viewport"
    content="width=device-width, initial-scale=1.0, user-scalable=no, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>

</head>

<body>
  <div id="app">
    {{message}}
    {{message}}
    {{message}}

    {{name}}
  </div>
</body>

<script>
  // TODO: 1-Object.defineProperty 监听属性改变, 并收集依赖项

  // 这就是data对象
  const obj = {
    message: '吕文琳,你好', // 一个属性对应一个dep对象, 每个对象里都有一个订阅数组(subs), 数组里面就存放着这个属性的, 观察者
    name: 'lvl'
  }

  // vue内部会先将这个对象内的key全部取出来,进行遍历
  Object.keys(obj).forEach(key => {
    // 拿到key对应的值
    let value = obj[key]

    // 此时使用Object.defineProperty函数对obj进行操作 vue3是使用 proxy
    Object.defineProperty(obj, key, {
      // 属性访问器
      set(newValue) {
        // 在set中可以监听属性的改变
        // 此时属性已经改变了, 该通知使用它的地方进行改变了? 怎么通知? 
        console.log("监听" + key + "值,发生了改变");
        value = newValue


        // 在属性发生改变的时候, 调用 dep.notify() 通知函数
      },
      get() {
        // 在get中可以监听获取key值
        console.log('获取' + key + '的值');


        // 张三 ==> update
        // 李四 ==> update
        // 王五 ==> update
        // 我们可以依靠 get 收集依赖
        return value
      }
    })

  })

  //TODO: 2-发布订阅者模式  

  // 发布者
  class Dep {
    // 构造器
    constructor() {
      // 在构造器里面有一个订阅数组 => subscribe
      this.subs = []
    }

    // 这里wather怎么来的?
    addSub(watcher) {
      // 一旦watcher发生改变, 那么就将watcher追加到订阅者数组中去
      this.subs.push(watcher)
    }

    // 通知函数
    notify() {
      this.subs.forEach(t => {
        t.update()
      })
    }
  }

  // 订阅者
  class watcher {
    constructor(name) {
      this.name = name
    }

    // 更新函数
    update() {
      console.log(this.name + '发生了改变');
    }
  }

  const dep = new Dep()

  const w1 = new watcher('张三')
  dep.subs.push(w1)

  const w2 = new watcher('李四')
  dep.subs.push(w2)

  const w3 = new watcher('王五')
  dep.subs.push(w3)


  dep.notify()
</script>

<script src="./node_modules/vue/dist/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: 'lwl么么哒',
      name: 'lwl'
    }
  })
</script>

</html>